package com.tcp2.net;


import com.tcp2.exception.LinkException;
import com.tcp2.server.DxsConfig;
import com.tcp2.server.MessageDispatcher;
import com.tcp2.server.ProtoMsg;
import com.tcp2.ui.MainFrame;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.net.InetSocketAddress;
import java.util.Date;
import java.util.concurrent.ConcurrentMap;

/**
 * 主链路
 * Author: 杨俊辉
 * Time: 2014-09-23 09:52
 * Copyright (C) 2014 Xiamen Yaxon Networks CO.,LTD.
 */

@Service
public class MasterLink {
    protected static Logger logger = LoggerFactory.getLogger(MasterLink.class);

    private DxsConfig   dxsConfig;
    private MessageDispatcher  messageDispatcher;

    private EventLoopGroup bossThreadPool;
    private EventLoopGroup ioThreadPool;
    private MasterLinkHandler masterLinkHandler;
    private UpDataHandler upDataHandler;
    private int ioThreads = Runtime.getRuntime().availableProcessors() * 2; //IO线程数
    private volatile int linkState = 0; //链路状态
    private static final int LS_STARTED = 1;
    private static final int LS_STOPPED = 2;
    private Channel serverChannel;
    private static final int CS_CONNECTED = 1;

    @Value("#{configProperties['dxs.vehicle.session.timeout']}")
    private Long vehicleSessionTimeout=600000l; //#可继续连接的时间间隔（默认1分钟）
    @Value("#{configProperties['dxs.limit.connect.time.interval']}")
    private Long limitConnectTimeInterval =60000l ;//限制建立连接时间间隔（默认时间1分钟 = 60*1000）
    @Value("#{configProperties['dxs.limit.connect.time.interval.max.count']}")
    private Integer limitConnectTimeIntervalMaxCount=5;//限制建立连接时间间隔内，累计最大连接次数（默认为5次）
    @Value("#{configProperties['dxs.continue.connect.time.interval']}")
    private Long continueConnectTimeInterval=3600000l; //可继续建立连接的时间间隔（默认1个小时 = 1*60*60*1000）

    private ProtoLogger protoLogger = new ProtoLogger("主链路");

    private Thread loopThread;


    class LoopThread extends Thread {
        LoopThread() {
            setName("master-link-loop-thread");
            setDaemon(true);
        }

        @Override
        public void run() {
            while (true) {


                //接入平台连接超时断开处理
                try {
                    MainFrame.MasterStatus=false;
                    ConcurrentMap<Integer, PlatformChannel> plat2channelMap = PlatformMap.getPlat2channelMap();
                    for (Integer gnssCenterId : plat2channelMap.keySet()) {
                        PlatformChannel plc = plat2channelMap.get(gnssCenterId);
                        Channel channel0 = plc.getChannel();
                        MainFrame.MasterStatus=channel0.isActive();
                        if (System.currentTimeMillis() - plc.getLastContactTime().get() > 180000) {
                            logger.info("接入平台连接超时，即将断开连接，平台接入号：:" + gnssCenterId);
                            Channel channel = plc.getChannel();
                            if (channel != null && channel.isActive()) {
                                channel.close();
                            }
                        }
                    }


                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    break;
                } catch (Exception e) {
                    logger.warn("主链路后台线程异常", e);
                }
            }
            logger.info("线程({})已停止", getName());
        }
    }


    /**
     * 保存链路通断记录
     * @param accessStatu  连接状态(0:离线 1:在线)
     * @param gnssCenterId  接入码
     * @param logType 上下线类型(1:上线 2:下线)
     */
    public void savePlatformLinkLog(Integer gnssCenterId, Byte accessStatu, Byte logType) {

    }

    public void setDxsConfig(DxsConfig dxsConfig) {
        this.dxsConfig = dxsConfig;
    }

    public DxsConfig getDxsConfig() {
        return dxsConfig;
    }

    public MessageDispatcher getMessageDispatcher() {
        return messageDispatcher;
    }

    public void setMessageDispatcher(MessageDispatcher messageDispatcher) {
        this.messageDispatcher = messageDispatcher;
        messageDispatcher.setMasterLink(this);
    }

    public Long getLimitConnectTimeInterval() {
        return limitConnectTimeInterval;
    }

    public Integer getLimitConnectTimeIntervalMaxCount() {
        return limitConnectTimeIntervalMaxCount;
    }

    public Long getContinueConnectTimeInterval() {
        return continueConnectTimeInterval;
    }



    /**
     * 开启通道
     *
     * @throws Exception
     */
    public void start() throws Exception {

        if (linkState == LS_STARTED) {
            return;
        }

        bossThreadPool = new NioEventLoopGroup(1);
        ioThreadPool = new NioEventLoopGroup(ioThreads);
        if (masterLinkHandler == null) {
            masterLinkHandler = new MasterLinkHandler(this, messageDispatcher, dxsConfig);
        }
        if (upDataHandler == null) {
            upDataHandler = new UpDataHandler(dxsConfig);
        }

        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossThreadPool, ioThreadPool)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(
                                    protoLogger,
                                    new ProtoMsgDecoder(dxsConfig),
                                    new ProtoMsgEncoder(dxsConfig),
                                    upDataHandler,
                                    masterLinkHandler);
                        }
                    });

            bootstrap.childOption(ChannelOption.SO_REUSEADDR, true);
            bootstrap.childOption(ChannelOption.TCP_NODELAY, true);
            bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);

            ChannelFuture cf = bootstrap.bind(new InetSocketAddress(dxsConfig.getBindPort())).sync();

            serverChannel = cf.channel();

            loopThread = new LoopThread();
            loopThread.start();

            linkState = LS_STARTED;
            logger.info("主链路服务已启动.", dxsConfig.getBindPort());
            MainFrame.ServerStatus=true;
        } catch (Throwable t) {
            logger.warn("启动链路服务失败!", dxsConfig.getBindPort());
        }
    }

    /**
     * 关闭通道
     */
    public void stop() {
        if (linkState == LS_STOPPED) {
            return;
        }

        try {
            if (serverChannel != null) {
                serverChannel.close().sync();
                serverChannel = null;
            }

            if (loopThread != null) {
                loopThread.interrupt();
                loopThread = null;
            }


            if (bossThreadPool != null) {
                bossThreadPool.shutdownGracefully();
                bossThreadPool = null;
            }

            if (ioThreadPool != null) {
                ioThreadPool.shutdownGracefully();
                ioThreadPool = null;
            }

            linkState = LS_STOPPED;
            logger.info("主链路服务已停止.");
            MainFrame.ServerStatus=false;
        } catch (Exception e) {
            logger.warn("停止主链路服务失败!", e);
        }
    }

    public void sendMessage(ProtoMsg msg, PlatformChannel platformChannel) throws LinkException {
        try {
            if (msg == null) {
                throw new RuntimeException("待发送的消息不能为null");
            }

            if (!isActive(platformChannel.getConnectState())) {
                throw new RuntimeException("主链路未建立");
            }
            masterLinkHandler.sendMessage(platformChannel.getChannel(), msg).sync();
        } catch (Exception e) {
            throw new LinkException("通过主链路发送消息失败", e);
        }
    }

    public boolean isActive(int connectState) {
        if (linkState == LS_STARTED && connectState == CS_CONNECTED) {
            return true;
        } else {
            return false;
        }
    }

}
